/******************************************************************************
 * Copyright (C) 2018-2021 aitos.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/*!@brief RLP Encoding header file

@file
boatrlp.h is the header file for RLP encoding.
*/

#ifndef __BOATRLP_H__
#define __BOATRLP_H__

#include "boatiotsdk.h"



/*!
Enum Type RlpObjectType
*/
typedef enum
{
    RLP_OBJECT_TYPE_STRING = 0,
    RLP_OBJECT_TYPE_LIST
}RlpObjectType;


#define RLP_STREAM_LEN_UNKNOWN (~0)
typedef struct TRlpEncodedStreamObject
{
    BUINT8 *stream_ptr;
    BUINT32 stream_len;
}RlpEncodedStreamObject;


typedef struct TRlpObjectTypeString
{
    BUINT8 *string_ptr;
    BUINT32 string_len;
}RlpObjectTypeString;


typedef struct TRlpObjectTypeList
{
    struct TRlpListDescriptors *list_descriptors_ptr;
    RlpEncodedStreamObject rlp_encoded_stream_object;
}RlpObjectTypeList;


typedef struct TRlpObject
{
    RlpObjectType object_type;
    
    union
    {
        RlpObjectTypeString object_string;
        RlpObjectTypeList   object_list;
    };
}RlpObject;


#define MAX_RLP_LIST_DESC_NUM 15
typedef struct TRlpListDescriptors
{
    BUINT32 descriptor_num;
    RlpObject *rlp_object_ptr[MAX_RLP_LIST_DESC_NUM];
}RlpListDescriptors;



#ifdef __cplusplus
extern "C" {
#endif

/*!*****************************************************************************
@brief Initialize an RLP object of STRING type

Function: RlpInitStringObject()

    This function initialize an RlpObject of string type. The caller allocates
    the memory for struct RlpObject and the string to hold by the RlpObject. DO
    NOT free the memory used by RlpObject and the string attached to the RlpObject.
    
    The RLP encoder takes care of the memory dynamically allocated during RLP
    encoding.

    NOTE: Despite its name being "string", it's actually byte stream.


@see RlpInitListObject()

@return
    This function returns BOAT_SUCCESS if successful.\n
    Otherwise it returns one of the error codes.

@param[in] rlp_object_ptr
    The pointer to a valid RlpObject.

@param[in] string_ptr
    Pointer of the string to attach to <rlp_object_ptr>.\n

@param[in] string_len
    Length (in byte) of <string_ptr>.

*******************************************************************************/
BOAT_RESULT RlpInitStringObject(RlpObject *rlp_object_ptr, BUINT8 *string_ptr, BUINT32 string_len);


/*!*****************************************************************************
@brief Initialize an RLP object of LIST type

Function: RlpInitListObject()

    This function initialize an RlpObject of list type. The caller allocates
    the memory for struct RlpObject. DO NOT free the memory used by RlpObject.
    
    The RLP encoder takes care of the memory dynamically allocated during RLP
    encoding.

    NOTE: The initial RlpObject of a LIST type is empty and can later attach
    other RlpObject of either STRING type or LIST type into it.


@see RlpInitStringObject()

@return
    This function returns BOAT_SUCCESS if successful.\n
    Otherwise it returns one of the error codes.

@param[in] rlp_object_ptr
    The pointer to a valid RlpObject.


*******************************************************************************/
BOAT_RESULT RlpInitListObject(RlpObject *rlp_object_ptr);


/*!*****************************************************************************
@brief Append an RLP object to a parent RLP object of LIST type

Function: RlpEncoderAppendObjectToList()

    This function appends an RlpObject (of either STRING type or LIST type) to
    of a parent RlpObject of LIST type.


@return
    This function returns non-negative descriptor index where the RlpObject is\n
    appended if successful.\n
    Otherwise it returns one of the error codes.

@param[in] to_list_object_ptr
    The parent LIST RlpObject to append to.

@param[in] from_object_ptr
    The child RlpObject to append.

*******************************************************************************/
BOAT_RESULT RlpEncoderAppendObjectToList(RlpObject *to_list_object_ptr, RlpObject *from_object_ptr);


/*!*****************************************************************************
@brief Replace an RLP object in a parent RLP object of LIST type

Function: RlpEncoderReplaceObjectInList()

    This function replaces an RlpObject at the specified index in a parent
    LIST RlpObject with another RlpObject.

    NOTE: The caller takes case of the memroy used by replaced RlpObject.


@return
    This function returns the replaced RlpObject's descriptor index in the parent
    LIST RlpObject if successful.\n
    Otherwise it returns one of the error codes.

@param[in] to_list_object_ptr
    The parent LIST RlpObject to append to.

@param[in] replace_index
    The descriptor index in <to_list_object_ptr> to replace.

@param[in] from_object_ptr
    The RlpObject to replace with.

*******************************************************************************/
BSINT32 RlpEncoderReplaceObjectInList(RlpObject *to_list_object_ptr, BSINT32 replace_index, RlpObject *from_object_ptr);


/*!*****************************************************************************
@brief Get an RLP object pointer in a parent RLP object by its index

Function: RlpEncoderGetListDescriptor()

    This function returns the RlpObject pointer at the specified index in a parent
    LIST RlpObject.

    NOTE: This doesn't remove the RlpObject from the parent LIST RlpObject. DO NOT
    free the memroy used by the RlpObject. DO NOT modify the content of the
    RlpObject.


@return
    This function returns the pointer to the RlpObject specified by the descriptor\n
    index in the parent if successful.\n
    Otherwise it returns NULL.

@param[in] rlp_object_ptr
    The parent LIST RlpObject.

@param[in] descriptor_index
    The descriptor index to get the RlpObject.

*******************************************************************************/
RlpObject * RlpEncoderGetListDescriptor(RlpObject *rlp_object_ptr, BUINT32 descriptor_index);


/*!*****************************************************************************
@brief Delete an RLP object and all its children

Function: RlpRecursiveDeleteObject()

    This function deletes an RlpObject and all its children RlpObject (if it's
    of LIST and there should be some children).

    NOTE: The caller takes care of the memory used by the struct RlpObject.


@see RlpRecursiveDeleteEncodedStream()

@return
    This function doesn't return anything.


@param[in] rlp_object_ptr
    The parent RlpObject to delete.

*******************************************************************************/
void RlpRecursiveDeleteObject(RlpObject *rlp_object_ptr);


/*!*****************************************************************************
@brief Delete encoded RLP stream in an RLP object and all its children

Function: RlpRecursiveDeleteEncodedStream()

    This function deletes encoded RLP stream in an RlpObject and all its children
    RlpObject (if it's of LIST and there should be some children).

    This function only deletes the encoded RLP stream while leaves the RlpObject's
    descriptor as is. Thus the tree-ed structure of its children is not affected.

    This function is used in an RLP re-encoding operation. It cleans all previously
    encoded RLP stream and prepares for a re-encoding.


@see RlpRecursiveDeleteObject()

@return
    This function doesn't return anything.


@param[in] rlp_object_ptr
    The parent RlpObject which contains the encoded RLP stream to delete.

*******************************************************************************/
void RlpRecursiveDeleteEncodedStream(RlpObject *rlp_object_ptr);


/*!*****************************************************************************
@brief Calculate the encoded RLP stream size in an RLP object and all its children

Function: RlpRecursiveCalcEncodingSize()

    This function calculates the size of the encoded RLP stream in an RlpObject
    and all its children RlpObject (if it's of LIST and there should be some children).

    The size returned is the total size of the stream that would be encoded by
    RlpEncode(). It's used to allocate memory before executing an RLP encoding
    operation.

    NOTE: This function doesn't actually encode the stream and doesn't allocate
    any dynamic memory.
    

@see RlpEncode()

@return
    This function returns the size of the encoded stream.


@param[in] rlp_object_ptr
    The RlpObject to be encode.

@param[out] rlp_head_size_ptr
    The RLP head size within the encoded stream.
    
*******************************************************************************/
BUINT32 RlpRecursiveCalcEncodingSize(RlpObject *rlp_object_ptr, BOAT_OUT BUINT8 *rlp_head_size_ptr);


/*!*****************************************************************************
@brief Encode an RLP stream as per RLP encoding rules.

Function: RlpEncode()

    This function recursively encodes an RLP stream for the specified RlpObject
    and its children (if there be).

    
    There are 3 possible types of RLP encoding structure as per RLP rules:
    1. encoded = <field>, if field_len is 1
    2. encoded = <1 byte prefix>|<field>, if field_len is in range [0,55] except 1
    3. encoded = <1 byte prefix>|<field_len>|<field>, if field_len >= 56
    where "|" means concatenaion.

    If encoding completes successfully, call RlpGetEncodedStream() to get the
    encoded RLP stream.

    If the RlpObject is no longer required, call RlpRecursiveDeleteObject() to
    delete all tree-ed RlpObject.

@note See https://github.com/ethereum/wiki/wiki/RLP for details of the encoding rule.

@see RlpRecursiveCalcEncodingSize() RlpGetEncodedStream() RlpRecursiveDeleteObject()

@return 
    This function returns BOAT_SUCCESS if successful.\n
    Otherwise it returns one of the error codes.


    @param[in] rlp_object_ptr
        The RlpObject to encode.

    @param[in] parent_storage_ptr
        The parent stream object to store the encoded RLP stream.\n\n
        If the <rlp_object_ptr> is the most outer LIST RlpObject, <parent_storage_ptr>\n
        can be specified with NULL, which allows the RLP encoder to automatically\n
        calculate the stream size and allocate memory for it.\n\n
        For a STRING RlpObject, the caller must calculate its size with\n
        RlpRecursiveCalcEncodingSize() and allcoate <parent_storage_ptr> for it. 

*******************************************************************************/
BOAT_RESULT RlpEncode(RlpObject *rlp_object_ptr, RlpEncodedStreamObject *parent_storage_ptr);


/*!*****************************************************************************
@brief Re-encode an RLP stream as per RLP encoding rules.

Function: RlpReEncode()

    This function recursively cleans previously encoded RLP stream for the
    specified RlpObject and re-encode the RLP stream.

    
@see RlpEncode()

@return 
    This function returns BOAT_SUCCESS if successful.\n
    Otherwise it returns one of the error codes.


    @param[in] rlp_object_ptr
        The RlpObject to encode.

    @param[in] parent_storage_ptr
        The parent stream object to store the encoded RLP stream.\n\n
        If the <rlp_object_ptr> is the most outer LIST RlpObject, <parent_storage_ptr>\n
        can be specified with NULL, which allows the RLP encoder to automatically\n
        calculate the stream size and allocate memory for it.\n\n
        For a STRING RlpObject, the caller must calculate its size with\n
        RlpRecursiveCalcEncodingSize() and allcoate <parent_storage_ptr> for it. 

*******************************************************************************/
BOAT_RESULT RlpReEncode(RlpObject *rlp_object_ptr, RlpEncodedStreamObject *parent_storage_ptr);


/*!*****************************************************************************
@brief Get the encoded RLP stream

Function: RlpGetEncodedStream()

    This function returns encoded RLP stream of the specified RlpObject.
    
    Make sure RlpEncode() or RlpReEncode() has been successfully called for
    the RlpObject.

    
@see RlpEncode() RlpReEncode()

@return 
    This function returns pointer to the encoded RLP stream object.\n
    Otherwise it returns NULL.


    @param[in] rlp_object_ptr
        The encoded RlpObject.

*******************************************************************************/
RlpEncodedStreamObject * RlpGetEncodedStream(RlpObject *rlp_object_ptr);


#ifdef __cplusplus
}
#endif /* end of __cplusplus */

#endif
